那麼動物的原型因為是比較高的階層,所以我們又可以有別的動物來繼承動物的原型,在上一張的最後則是由貓的原型來繼承
好~說到這裡感覺很像繞口令齁~ 我們用下面這張表來看一下這些東西的互相關係
那麼為了讓畫面簡潔一點呢,這邊沒有顯示出貓的實體以及貓的原型!
另外呢畫面上有綠色字體顯示的,Dog、Animal、Object、Function,這些呢都是屬於建構函式
首先呢我們先專注在最左上方的比比(狗的實體),如果我們使用console.log來看比比的話,就會顯示出這樣的結果。
那麼比比的內容呢,有包含了 name、family、color以及這個章節介紹的重點 __proto__
。
這個 __proto__
會指向狗的原型,而狗的原型就是透過狗的建構函式而來,所以狗的原型的 constructor 會指向 狗的建構函式。
同時狗的原型的 __proto__
也可以向上尋找,找到動物的原型。
每一個原型都會有一個 constructor 去指向這個建構函式。
所以動物的原型的 constructor 就會去指向動物的建構函式
然後動物的 __proto__
也可以向上尋找,找到物件的原型。
然後物件的原型的 constructor 也會連結到 物件的建構函式。
如果物件的建構函式的 __proto__
繼續向上尋找,最後會找到 null 的結果。
了解到這裡,其實你已經掌握了絕大部分的概念,那麼我們再多說一些觀念吧!
首先先把剛剛的部分調淡一些些。
剛剛的 Dog、Animal、Object 都可以利用 Dog、Animal、Object.prototype 來產生原型或是將方法掛載在這些建構函式的原型鏈上。
那麼為什麼,我們可以使用 prototype 這個方法呢?
那是因為 Dog、Animal、Object 的 __proto__
都是繼承於 Function 這個建構函式的 prototype。
最後我們再來看一下 函式, Function 的原型。
Function 的原型的 __proto__
繼續向上尋找,一樣會找到物件的原型。
Function 的原型的 constructor 會指向 Function 的建構函式。
Function 的建構函式的 __proto__
則會指向回 Function 的原型。
function Animal (family) {
this.kingdom = '動物界';
this.family = family || '人科';
}
Animal.prototype.move = function () {
console.log(this.name + ' 移動');
};
function Dog (name, color, size) {
Animal.call(this, '犬科');
this.name = name;
this.color = color || '白色';
this.size = size || '小';
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function () {
console.log(this.name + '吠叫');
}
var Bibi = new Dog('比比', '棕色', '小');
console.log(Bibi.__proto__ === Dog.prototype);
透過這樣的結果我們可以知道 這個 proto
會指向狗的原型,而狗的原型就是透過狗的建構函式而來,這個觀念是對的。
那我們繼續再加上~
console.log(Bibi.__proto__.__proto__ === Animal.prototype);
所以往上找兩層也就是 狗實體 >> 狗原型 >> 動物原型,而答案也是 true。
那我們繼續再加上~
console.log(Bibi.__proto__.__proto__.constructor === Animal);
那麼 動物原型 的 建構器 很明顯就應該要是動物的建構函式,答案一樣也是 true,代表的確動物的原型的 constructor 是指向動物的建構函式。
那我們繼續再加上~
console.log(Bibi.__proto__.__proto__.__proto__.__proto__ === null);
那麼驗證的結果也是 true。
那我們繼續再加上~
console.log(Dog.__proto__ === Function.prototype);
console.log(Animal.__proto__ === Function.prototype);
console.log(Object.__proto__ === Function.prototype);
__proto__
是否指向 Function 的原型那我們繼續再加上~
console.log(Function.__proto__ === Function.prototype);
同時,Function 的原型再向上尋找就會找到物件的原型喔!
那我們繼續再加上~
console.log(Function.__proto__.__proto__ === Object.prototype);
所以這個觀念也被驗證!
以上就是原型鏈以及繼承的關係,也可以參考 MDN 繼承與原型鏈的文章
來更深入的學習喔!
沒問題的話就可以來做做後面的習題喔!